﻿using Haha.Att;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading.Tasks;

namespace Haha.Mvc
{
    public class MvcInit
    {

        private MvcInit() { }
        private static MvcInit me = new MvcInit();
        public static MvcInit New
        {
            get
            {
                return me;
            }
        }
        private  Dictionary<string, ActionInfo> actions = new Dictionary<string, ActionInfo>();
        //private static List<string> apps = new List<string>();
        private  Dictionary<string, bool> apps = new Dictionary<string, bool>();
        public static MvcInit Init(Type type)
        {
           
            foreach(String k in  System.Configuration.ConfigurationSettings.AppSettings.Keys){
                me.Cfg.SetV(k, System.Configuration.ConfigurationSettings.AppSettings[k]);
            }

            me.init(type);
            return me;
        }
        private HahaMap cfg = new HahaMap();
        public HahaMap Cfg
        {
            get
            {
                return cfg;
            }
        }
        private void init(Type type)
        {
            Type[] types = type.Assembly.GetTypes();

            foreach (Type t in types)
            {
                var atts = t.GetCustomAttributes(false);
                if (atts.Where(d => d.GetType() == typeof(Haha.Att.ActionAttribute)).Count() > 0)
                {
                    var att = atts.Where(d => d.GetType() == typeof(Haha.Att.ActionAttribute)).Single();
                    string path = GetAtt(att, "path");
                    string app = GetAtt(att, "app");

                    if (app.Length != 0)
                    {
                        path = "/" + app +"/"+ path;
                    }
                    
                    if (apps.ContainsKey(app) == false)
                    {
                        apps.Add(app, false);
                    }
                    
                    actions.Add(path, ActionInfo.New(path, t, app));

                }
            }
        }

        public static bool ExitsApp(String app)
        {
            return me.apps.ContainsKey(app);
        }
        public static string GetAtt(Object att, string p)
        {
            //var list=att .NamedArguments.ToList().Where(d=>d.MemberName.ToLower()==p.ToLower()).ToList();
            //return list.Count == 0 ? "" : list[0].TypedValue.Value.ToString().ToLower();
            if (att is MvcAttribute)
            {
                var attr = att as MvcAttribute;
                if (p.ToLower() == "path") return attr.Path==null?"":attr.Path;
                if (p.ToLower() == "type") return attr.Type==null?"":attr.Type;
            }
            if (att is MvcImageAttribute)
            {
                var attr = att as MvcImageAttribute;
                if (p.ToLower() == "imagetype") return attr.ImageType==null?"":attr.ImageType;
            }
            if (att is ActionAttribute)
            {
                var attr = att as ActionAttribute;
                if (p.ToLower() == "path") return attr.Path == null ? "" : attr.Path;
                if (p.ToLower() == "app") return attr.App == null ? "" : attr.App;
            }
            if (att is LayoutAttribute)
            {
                var attr = att as LayoutAttribute;
                if (p.ToLower() == "layoutname") return attr.LayoutName == null ? "" : attr.LayoutName;
                if (p.ToLower() == "title") return attr.Title == null ? "" : attr.Title;
            }
            return "";
        }



        public  ActionMethodInfo Parse(System.Web.HttpContext context, string actionName, string methodName)
        {
            ActionMethodInfo method = new ActionMethodInfo();
            var req = context.Request;
            var resp = context.Response;
            var app = context.Application;
            MvcContext.Init(context);
            if (actions.ContainsKey(actionName) == false) return method.SetRestult("");
            ActionInfo act = actions[actionName];
             method = act.GetActionMethodInfo(methodName);
             if (method == null) return new ActionMethodInfo().SetRestult("");
            if (method.Type == "json")
            {
                return method.SetRestult( act.CallMethod(method).ToString());
            }
            if (method.Type == "text")
            {
                return method.SetRestult(act.CallMethod(method).ToString());
            }
            if (method.Type == "stream")
            {
                return method.SetRestult(act.CallMethod(method));
            }
            
            if(apps[act.App]==false)
                InitTemp(req.MapPath("~/"+act.App+"temp"),act.App);
            var retObj = act.CallMethod(method);

            if (retObj is MvcView)
            {
                MvcView mv = retObj as MvcView;
                string path = mv.Url + ".cshtml";
               
                string data = GetFile(path, context);
                if(data==null) return method.SetRestult("");
                switch (mv.MvcViewType)
                {
                    case MvcViewType.Json:
                        return method.SetRestult(mv.Obj.ToString());
                    case MvcViewType.Text:
                        return method.SetRestult(mv.Obj.ToString());
                    case MvcViewType.Strean:
                        return method.SetRestult(mv.Obj);
                }
                return method.SetRestult(RazorEngine.Razor.Parse(data, new { Obj = mv.Obj, Cfg = Cfg, Title=method.Title }));
            }
            else
            {
                return ParseCshtml(context, method, act, retObj);
            }
        }
        private ActionMethodInfo ParseCshtml(System.Web.HttpContext context, ActionMethodInfo method, ActionInfo act, object retObj)
        {
            string path = "~" + act.Path + "/" + method.Opt + ".cshtml";
            string data = GetFile(path, context);
            if (data == null) return method.SetRestult("");
            if ( method.LayoutName.Length > 0)
            {
                //load layout
                string layoutPath = MvcInit.New.Cfg[act.App + "layoutPath"].ToString()+method.LayoutName;
                string layoutData = GetFile(layoutPath, context);
                data = layoutData.Replace("$pagecontext", data);
            }
            return method.SetRestult(RazorEngine.Razor.Parse(data, new { Obj = retObj, Cfg = Cfg, Title = method.Title }));
        }
        public string GetFile(string path, System.Web.HttpContext context)
        {
            path = context.Server.MapPath(path);
            if (File.Exists(path) == false) return null;
            StreamReader sr = new StreamReader(path, Encoding.UTF8);
            String data = sr.ReadToEnd();
            sr.Close();
            return data;
        }

        private  void InitTemp(String path,string app)
        {
            apps[app] = true;
            if (Directory.Exists(path)==false) return;
            var files=Directory.GetFiles(path);
            foreach (var f in files)
            {
                FileInfo fi = new FileInfo(f);
                String name = fi.Name;
                name = name.Substring(0, name.LastIndexOf("."));
                var header = System.IO.File.ReadAllText(f, Encoding.UTF8);
                RazorEngine.Razor.Compile(header, name);
            }
        }
    }
    public class ActionInfo
    {
        public ActionMethodInfo GetActionMethodInfo(string methodName)
        {
            if (Methods.ContainsKey(methodName))
            {
                return Methods[methodName];
            }
            return null; 
        }
        public string App { get; set; }
        public string Path { get; set; }
        public Type Type{ get; set; }
        public object Object { get; set; }
        private Dictionary<string, ActionMethodInfo> Methods = new Dictionary<string, ActionMethodInfo>();
        public static ActionInfo New(string path, System.Type t,string app)
        {
            ActionInfo ac = new ActionInfo();
            ac.Path = path;
            ac.App = app;
            ac.Type = t;
            ac.Object = System.Activator.CreateInstance(t);

            return ac.InitMethods();
        }
        public ActionInfo InitMethods()
        {
            MethodInfo[] mms = Type.GetMethods();
            foreach (var mm in mms)
            {
               
                if (mm.GetCustomAttributes(false).Where(d => d.GetType() == typeof(Haha.Att.MvcDownAttribute)).Count() > 0)
                {
                    var att = mm.GetCustomAttributes(false).Where(d => d.GetType() == typeof(Haha.Att.MvcDownAttribute)).Single();
                    ActionMethodInfo am = new ActionMethodInfo();
                    am.MethodInfo = mm;
                    am.App = this.App;
                    am.ContextType = "application/octet-stream";
                    am.Type = "stream";
                    am.Opt = "";
                    if (am.Opt.Length == 0)
                    {
                        am.Opt = mm.Name;
                    }
                    Methods.Add(am.Opt, am);
                    
                }
                if (mm.GetCustomAttributes(false).Where(d => d.GetType() == typeof(Haha.Att.MvcImageAttribute)).Count() > 0)
                {
                    var att = mm.GetCustomAttributes(false).Where(d => d.GetType() == typeof(Haha.Att.MvcImageAttribute)).Single();
                    ActionMethodInfo am = new ActionMethodInfo();
                    am.MethodInfo = mm;
                    am.App = this.App;
                    am.ImageType = MvcInit.GetAtt(att, "imagetype");
                    am.ContextType = "image/"+am.ImageType;
                    am.Type = "stream";
                    am.Opt = "";
                    if (am.Opt.Length == 0)
                    {
                        am.Opt = mm.Name;
                    }
                    Methods.Add(am.Opt, am);
                    
                }
                if (mm.GetCustomAttributes(false).Where(d => d.GetType() == typeof(Haha.Att.MvcJsonAttribute)).Count() > 0)
                {
                    var att = mm.GetCustomAttributes(false).Where(d => d.GetType() == typeof(Haha.Att.MvcJsonAttribute)).Single();
                    ActionMethodInfo am = new ActionMethodInfo();
                    am.MethodInfo = mm;
                    am.App = this.App;
                    am.Opt ="";
                    am.Type = "json";
                    if (am.Opt.Length == 0)
                    {
                        am.Opt = mm.Name;
                    }
                    Methods.Add(am.Opt, am);
                    
                }
                if (mm.GetCustomAttributes(false).Where(d => d.GetType() == typeof(Haha.Att.MvcTextAttribute)).Count() > 0)
                {
                    var att = mm.GetCustomAttributes(false).Where(d => d.GetType() == typeof(Haha.Att.MvcTextAttribute)).Single();
                    ActionMethodInfo am = new ActionMethodInfo();
                    am.MethodInfo = mm;
                    am.App = this.App;
                    am.Opt = "";
                    am.Type = "text";
                    if (am.Opt.Length == 0)
                    {
                        am.Opt = mm.Name;
                    }
                    Methods.Add(am.Opt, am);
                    
                }
                

                if (mm.GetCustomAttributes(false).Where(d => d.GetType() == typeof(Haha.Att.MvcAttribute)).Count() > 0)
                {
                    var att = mm.GetCustomAttributes(false).Where(d => d.GetType() == typeof(Haha.Att.MvcAttribute)).Single();
                    ActionMethodInfo am = new ActionMethodInfo();
                    am.MethodInfo = mm;
                    am.App = this.App;
                    am.Opt = MvcInit.GetAtt(att,"path");
                    am.Type = MvcInit.GetAtt(att, "type");
                    
                    if (am.Opt.Length == 0)
                    {
                        am.Opt = mm.Name;
                    }
                    Methods.Add(am.Opt,am);
                }

                if (mm.GetCustomAttributes(false).Where(d => d.GetType() == typeof(Haha.Att.LayoutAttribute)).Count() > 0)
                {
                    var att = mm.GetCustomAttributes(false).Where(d => d.GetType() == typeof(Haha.Att.LayoutAttribute)).Single();
                    String name = mm.Name;
                    ActionMethodInfo am = null;
                    if (Methods.ContainsKey(name))
                    {
                        am = Methods[name];
                    }
                    else
                    {
                        am.App = this.App;
                        am = new ActionMethodInfo();
                        am.MethodInfo = mm;
                        am.Opt = mm.Name;
                        Methods.Add(am.Opt, am);
                    }
                    am.LayoutName = MvcInit.GetAtt(att, "layoutname");
                    string title = MvcInit.GetAtt(att, "title");
                    if (string.IsNullOrEmpty(title) == false)
                    {
                        am.Title = title;
                    }
                    if (am.LayoutName.Length == 0)
                    {
                        am.LayoutName = MvcInit.New.Cfg[am.App + "defaultLayer"].ToString();
                    }

                }
               
            }
            return this;
        }
        public object CallMethod(ActionMethodInfo am)
        {
            return am.MethodInfo.Invoke(this.Object,new Object[]{});
        }
    }

    public class ActionMethodInfo
    {
        public ActionMethodInfo() {
            Opt = Type = App = ImageType = ContextType = LayoutName = "";
            Title = (String)MvcInit.New.Cfg["title"];
            if (String.IsNullOrEmpty(Title)) Title = "未命名";
        }
        public MethodInfo MethodInfo { get; set; }
        public string Opt { get; set; }

        public string Type { get; set; }
        public string App { get; set; }

        public string ImageType { get; set; }

        public string ContextType { get; set; }

        public object Restult { get; set; }

        public string LayoutName { get; set; }

        public string Title { get; set; }
        public ActionMethodInfo SetRestult(object obj)
        {
            this.Restult = obj;
            return this;

        }
        
    }


}
